/* 
 * File:   UpdateTable.cpp
 * Author: vikrantsingh
 * 
 * Created on 1 December, 2013, 3:38 AM
 */

#include "UpdateTable.h"
#include "../dependency/GlobalDefines.h"
#include "../dependency/debug.h"
#include "Validation.h"
#include "../Sysfiles/SysCatalog.h"
#include "../PageLayout/Tuple.h"
#include "TableScan.h"
#include "PostFixEvaluator.h"
#include "../IndexManager/BPlusTree.h"
#include "../IndexManager/IndexUtil.h"
#include "SelectTable.h"
#include "../dependency/util.h"

UpdateTable::UpdateTable() {
}

UpdateTable::UpdateTable(const UpdateTable& orig) {
}

UpdateTable::~UpdateTable() {
}

STATUS_CODE UpdateTable::run(QueryStructures *queryStruct) {
    DEBUG("inside update execute");
    //	cout<<"tableName"<<queryStruct->tableName.c_str();
    int error = Validation::isValid();
    if (SUCCESS != error) {
        return error;
    }
    SysCatalog cat;
    Schema schema;
    error = cat.getSchema(queryStruct->tableName.c_str(), schema);
    if (error == TABLE_NOT_FOUND) {
        return error;
    }

    Heap heapFile(schema.tableName.c_str());

    //////////////////////////////////////////
    //////////////////////////////////////////////////
    int tableId = 0;
    cat.getTableId(queryStruct->tableName.c_str(), tableId);

    whereConditionExprList_.assign(
            queryStruct->whereConditionExprList.begin(),
            queryStruct->whereConditionExprList.end());
    resultFromIndex = false;
    _currentRidIndexSearch = 0;
    _ridList.clear();
    bool indexExists = false;
    bool isORContains = false;
    int indexHeaderPageNum = -1;
    vector<int> keyColNo;
    //std::vector<int> indexHeaderPageNumberList;
    vector<string> indexedColumn;

    for (unsigned i = 0; i < whereConditionExprList_.size(); ++i) {
        if (whereConditionExprList_[i].type_ == WhereClause::OPERATOR_TYPE) {
            if (strcmp(whereConditionExprList_[i].operatorValue.c_str(), "OR") == 0) {
                isORContains = true;
                break;
            }
        }
        if (whereConditionExprList_[i].type_ == WhereClause::IDENTIFIER_TYPE) {
            indexedColumn.push_back(whereConditionExprList_[i].identifierValue);
        }
    }

    if (!isORContains && whereConditionExprList_.size() > 0) {
        indexExists = cat.getBestIndexIfExist(schema, indexedColumn, indexHeaderPageNum, tableId, keyColNo);
    }

    // check is index exists
    /*
 PICK BEST INDEX
     *    ITERATE ALL INDEX
     *      CHECK IF ALL KEY ATTRIBUTE ARE IN WHERE
     *      IF NO WE CAN'T USE INDEX
     *      IF YES CHECK WHO MANY FROM WHERE CLAUSE SELECT MAX ONE

     */

//    cout << "\n is Index Exists :" << indexExists << endl;
    if (indexExists == true && USING_INDEX == true && isORContains == false && indexHeaderPageNum != -1) {

//        cout << "\n Index header Page no : " << indexHeaderPageNum << endl;

        IndexHeaderPage indexHeaderPage(indexHeaderPageNum);
        //        cout << "in index check1" << endl;
        BPlusTree bplusTree(indexHeaderPageNum);
        //        cout << "in index check1" << endl;
        int keySize = indexHeaderPage.getKeySize();


        vector<string> columnList;
        vector<string> literalList;
        vector<int> operatorList;
        
        for (unsigned j = 0; j < keyColNo.size(); j++) {
//            cout << " For key Col No" << keyColNo[j] << endl;
            for (unsigned i = 0; i < whereConditionExprList_.size(); i++) {

                if (whereConditionExprList_[i].type_ == WhereClause::IDENTIFIER_TYPE) {
                    if (schema.getColumnNum(whereConditionExprList_[i].identifierValue.c_str()) == keyColNo[j]) {
                        ///////colName
                        columnList.push_back(whereConditionExprList_[i].identifierValue);
                        //////Literal
                        if (whereConditionExprList_[i + 1].type_
                                == WhereClause::LITERAL_TYPE) {
                            literalList.push_back(whereConditionExprList_[i + 1].literalValue);
                        }
                        ///////operator
                        if (whereConditionExprList_[i + 2].type_
                                == WhereClause::OPERATOR_TYPE) {
                            if (whereConditionExprList_[i + 2].operatorValue == "=") {
                                int op = 1; //EQUAL_TO;
                                operatorList.push_back(op);
                            } else if (whereConditionExprList_[i + 2].operatorValue == "<") {
                                int op = 4; //LESS_THAN;
                                operatorList.push_back(op);
                            } else if (whereConditionExprList_[i + 2].operatorValue == ">") {
                                int op = 2; //GREATER_THAN;
                                operatorList.push_back(op);
                            } else if (whereConditionExprList_[i + 2].operatorValue == "<=") {
                                int op = 5; //LESS_THAN_OR_EQUAL_TO;
                                operatorList.push_back(op);
                            } else if (whereConditionExprList_[i + 2].operatorValue == ">=") {
                                int op = 3; //GREATER_THAN_OR_EQUAL_TO;
                                operatorList.push_back(op);
                            }
                        }
                    }

                }
            }
        }
//        cout << "\n column list:";
//        for (unsigned k = 0; k < columnList.size(); k++) {
//            cout << " " << columnList[k];
//        }
//        cout << "\n operator list:";
//        for (unsigned k = 0; k < operatorList.size(); k++) {
//            cout << " " << operatorList[k];
//        }
//        cout << "\n literal list:";
//        for (unsigned k = 0; k < literalList.size(); k++) {
//            cout << " " << literalList[k];
//        }
//        cout << "\n";
        char *keyData = new char[keySize];
        int offset = 0;
        for (unsigned i = 0; i < columnList.size(); i++) {
            int fieldNum = schema.getColumnNum(columnList[i]);
            if (schema.fieldTypes.at(fieldNum) == TYPE_INTEGER) {
                int recordData = Util::string_to_int(literalList[i]);
                DEBUG("keyData" << recordData);
                memcpy(&keyData[offset], &recordData,
                        schema.fieldLengths[fieldNum]);
                offset = offset + schema.fieldLengths[fieldNum];
            } else if (schema.fieldTypes.at(fieldNum) == TYPE_FLOAT) {
                float recordData = Util::string_to_float(literalList[i]);
                DEBUG("keyData" << recordData);
                memcpy(&keyData[offset], &recordData,
                        schema.fieldLengths[fieldNum]);
                offset = offset + schema.fieldLengths[fieldNum];
            } else if (schema.fieldTypes.at(fieldNum) == TYPE_VARCHAR) {
                const char *recordData = literalList[i].c_str();
                memcpy(&keyData[offset], recordData,
                        schema.fieldLengths[fieldNum]);
                offset = offset + schema.fieldLengths[fieldNum];
            }
        }
        //        cout << "in index check4" << endl;
        //bplusTree.search(keyData, _ridList);
//        cout << "\n keydata:" << IndexUtil::keyToString(keyData, &indexHeaderPage) << endl;
        bplusTree.search(keyData, operatorList, _ridList);
        resultFromIndex = true;
        delete keyData;
        //        if (USE_CLIENT_SIDE_SORT == true) {
        //            sort(_ridList.begin(), _ridList.end());
        //        }

        //        cout<<"\nRID LIST SIZE"<< _ridList.size()<<endl;
        for (unsigned j = 0; j < _ridList.size(); j++) {
            char *record;
            unsigned recordLen;
            heapFile.getRecord(_ridList[j], recordLen, record);
            Tuple tuple(schema, record, recordLen);
            if (queryStruct->whereConditionExprList.size() > 0) {

                PostFixEvaluator postFixEvaluator(tuple);

                if (postFixEvaluator.evaluate(queryStruct->whereConditionExprList) == true) {
                    //add to result set;
                    DEBUG("tuple" << tuple.toString());
                    //                     cout<<"\ntuple " << tuple.toString();                    
                    toUpdateRIDList.push_back(_ridList[j]);
                }

            }


        }
        //        cout << "in index check6" << endl;

        // return SUCCESS;
    }

    //////////////////////////////////////////////////


    /////////////////////////////////////////

    if (resultFromIndex == false) {

        TableScan tableScan(&heapFile);

        char *record;
        unsigned recordLen;
        int count = 0;
        //	cout<<"starting table scan";
        while (tableScan.getNextRec(record, recordLen) != SCAN_OVER) {
//            cout << "in while";
            Tuple tuple(schema, record, recordLen);
            if (queryStruct->whereConditionExprList.size() > 0) {
                //                    cout<<"in if";
                PostFixEvaluator postFixEvaluator(tuple);
                if (postFixEvaluator.evaluate(
                        queryStruct->whereConditionExprList) == true) {
                    //add to result set;
                    //				cout<<"to update rows="<<tuple.toString();
                    RIDStruct rid = tableScan.getRid();
                    toUpdateRIDList.push_back(rid);
                    count++;
                }
            } else {//no where conditions
                //add all to resultset
                //                    cout<<"in else";
                //			cout<<"to update rows="<<tuple.toString();
                RIDStruct rid = tableScan.getRid();
                toUpdateRIDList.push_back(rid);
                count++;
            }

        }
    }
    for (unsigned l = 0; l < toUpdateRIDList.size(); l++) {
        //		cout<<"to delete RID={"<<toUpdateRIDList[i].pageNumber<<","<<toUpdateRIDList[i].slotNumber<<"}";
        //get the record from heap file
        char *record;
        
        //char *record2 = new char[recordLen];
        unsigned recordLen;
        heapFile.getRecord(toUpdateRIDList[l], recordLen, record);
        char *record2 = new char[recordLen];
        char *newrecord2= new char[recordLen];
        memcpy(record2, record, recordLen);
        memcpy(newrecord2, record, recordLen);


        Tuple oldTuple(schema, record2, recordLen);
        Tuple newTuple(schema, newrecord2, recordLen);                                
        
        
        //set the relevant fields
        for (unsigned j = 0; j < queryStruct->updateQueryColumnNames.size(); j++) {
            newTuple.setFieldByConversion(
                    queryStruct->updateQueryColumnNames[j],
                    queryStruct->updateQueryColumnValues[j]);
        }
        //updateRecord
        //		cout<<"recordLen="<<recordLen;
        //                cout<<"AFTER update rows="<<tuple.toString();
        heapFile.updateRecord(toUpdateRIDList[l], recordLen, newrecord2);
        //                cout<<"AFTER update rows HEAP="<<tuple.toString();
                
        
/*
 * 
 *              UPDATE INDEX
 */    
        
         if (true) {

            //        cout<<"using index";
            int tableID;
            cat.getTableId(schema.tableName.c_str(), tableID);
            SelectTable select;
            QueryStructures toGetFromSysIndex;
            toGetFromSysIndex.resultSetColumnList.push_back("INDEX_ID");
            toGetFromSysIndex.resultSetColumnList.push_back("INDEX_HDR_PG_NO");
            toGetFromSysIndex.tableName = "SYSINDEX";
            WhereClause exprInfo1(WhereClause::IDENTIFIER_TYPE, "TAB_ID");
            WhereClause exprInfo2(WhereClause::LITERAL_TYPE, Util::int_to_string(tableID));
            WhereClause exprInfo3(WhereClause::OPERATOR_TYPE, "=");
            toGetFromSysIndex.whereConditionExprList.push_back(exprInfo1);
            toGetFromSysIndex.whereConditionExprList.push_back(exprInfo2);
            toGetFromSysIndex.whereConditionExprList.push_back(exprInfo3);
            int err = select.fetchResultSetAll_WithoutIndex(&toGetFromSysIndex);
            if (SUCCESS != err) {
                return err;
            }
            for (unsigned i = 0; i < select.resultSet.size(); i++) {
                //            cout<<"\ninside for result set size"<<select.resultSet.size();
                SelectTable selectQuerytoGetFromSysIndexCol;
                QueryStructures toGetFromSysIndexCol;
                toGetFromSysIndexCol.resultSetColumnList.push_back("SYS_COL_ID");
                toGetFromSysIndexCol.tableName = "SYSINDEXCOL";
                WhereClause exprInfo1(WhereClause::IDENTIFIER_TYPE, "INDEX_ID");
                WhereClause exprInfo2(WhereClause::LITERAL_TYPE, select.resultSet[i].getFeildInString("INDEX_ID"));
                WhereClause exprInfo3(WhereClause::OPERATOR_TYPE, "=");
                toGetFromSysIndexCol.whereConditionExprList.push_back(exprInfo1);
                toGetFromSysIndexCol.whereConditionExprList.push_back(exprInfo2);
                toGetFromSysIndexCol.whereConditionExprList.push_back(exprInfo3);
                err = selectQuerytoGetFromSysIndexCol.fetchResultSetAll_WithoutIndex(&toGetFromSysIndexCol);
                if (SUCCESS != err) {
                    return err;
                }

                //            cout<<"\n num of col"<<selectQuerytoGetFromSysIndexCol.resultSet.size()<<endl;
                int indexHeaderPageNum = Util::string_to_int(select.resultSet[i].getFeildInString("INDEX_HDR_PG_NO"));
                IndexHeaderPage indexHeaderPage(indexHeaderPageNum);
                //            cout<<"INSERT INTO INDEX HEADER PAGE NUM"<<indexHeaderPageNum<<endl;

                int keySize = indexHeaderPage.getKeySize();

                char *oldKeyData = new char[keySize];
                char *newKeyData = new char[keySize];
                vector<int> colIds;
                for (unsigned p = 0; p < selectQuerytoGetFromSysIndexCol.resultSet.size(); p++) {
                    colIds.push_back(Util::string_to_int(selectQuerytoGetFromSysIndexCol.resultSet[p].getFeildInString("SYS_COL_ID")));
                }

                oldTuple.getKeyData(colIds, oldKeyData);
                newTuple.getKeyData(colIds, newKeyData);
//                cout << "keyData:" << IndexUtil::keyToString(oldKeyData, &indexHeaderPage) << endl;
                //            cout<<"INSERT INTO INDEX HEADER PAGE NUM"<<indexHeaderPageNum<<endl;
                BPlusTree bplusTree(indexHeaderPageNum);

                //            cout<<"\nRID="<<rid.toString();
                bplusTree.remove(oldKeyData, toUpdateRIDList[l]);
                bplusTree.insert(newKeyData, toUpdateRIDList[l]);

                delete oldKeyData;
                delete newKeyData;
            }


        }
                                                  
        delete record2;
        delete newrecord2;
    }
    
    cout<<"\n#No of Records Updated : "<<toUpdateRIDList.size()<<endl;
    return SUCCESS;

}